import os,tempfile,uuid,shutil,commands
import socket

import time
import logging
log = logging.getLogger(__name__)

class IProfileMaker(object):
    """
    This is the abstract interface that VMManager calls to produce the userdata/profile to give the VMs.
    
    @author Yushu Yao
    @version 0.1
    """

    def __init__(self,profiletype):
        self.profiletype=profiletype

    def provideProfile(self,role):
        raise NotImplementedError

    def provideReverseProfile(self,role):
        raise NotImplementedError

import pickle

class Puppet_ProfileMaker(IProfileMaker):
    def __init__(self):
        super(Puppet_ProfileMaker, self).__init__("puppet")

        #Map of "profileuuid->pathToTarredBase64file
        self.__profiles={}

        self.__nodesfile="/etc/puppet/manifests/nodes.pp"

    def debug(self,str):
        print "DEBUG "+str
        
    def provideProfile(self,role,reverse=False):
        if role.vm.getAttr("MetaDataProvided")!="True":
            self.provideMetaData(role)

        #assuming one role per VM
        pp=self.readFromFile()
        if pp==None:
            pp=Puppet_ProfileMaker.PP()
            
        thisnode=None
        for inode in pp.nodes:
            if str(inode.roleid)==str(role.Role_ID) and str(inode.vmid)==str(role.VM_ID):
                thisnode=inode
                break
        if thisnode==None:
            thisnode=Puppet_ProfileMaker.NODE()
            thisnode.roleid=role.Role_ID
            thisnode.vmid=role.vm.VM_ID
            pp.nodes.append(thisnode)
        thisnode.addr="http://%s:8080"%socket.gethostbyname(socket.gethostname())
        thisnode.hostname=role.vm.getAttr("PrivateHostname")

        if reverse:
            thisnode.profile=role.roleDef.profile+"_reverse"
        else:
            thisnode.profile=role.roleDef.profile

        self.saveToFile(pp)
        
    def provideReverseProfile(self,role):
        provideProfile(self,role,reverse=True)

    def readPPFromStr(self,strarray,pos):
        pp=None
        
        #first look for begin part
        cur=pos[0]
        while cur<len(strarray):
            spline=strarray[cur].split()
            if len(spline)>0 and spline[0].find("###")>=0:
                if spline[0]=="###BEGINPP":
                    pp=Puppet_ProfileMaker.PP()
                    break
                else:
                    return None
            cur=cur+1

        
        if cur>=len(strarray):
            return None

        pos[0]=cur+1
        node=None
        while True:
            node=self.readNODEFromStr(strarray,pos)
            if node==None:
                break
            else:
                pp.nodes.append(node)
                
        cur=pos[0]
        while cur<len(strarray):
            spline=strarray[cur].split()
            if len(spline)>0 and spline[0].find("###")>=0:
                if spline[0]=="###ENDPP":
                    return pp
                else:
                    return None
            cur=cur+1
            
        return None
            
    def readNODEFromStr(self,strarray,pos):
        node=None
        
        #first look for begin part
        cur=pos[0]
        while cur<len(strarray):
            spline=strarray[cur].split()
            if len(spline)>0 and spline[0].find("###")>=0:
                if spline[0]=="###BEGINNODE":
                    node=Puppet_ProfileMaker.NODE()
                    node.vmid=spline[1]
                    node.roleid=spline[2]
                    node.hostname=spline[3]
                    node.profile=spline[4]
                    node.addr=spline[5]
                    break
                else:
                    return None
            cur=cur+1

        if cur>=len(strarray):
            return None

        pos[0]=cur+1

        cur=pos[0]
        while cur<len(strarray):
            spline=strarray[cur].split()
            if len(spline)>0 and spline[0].find("###")>=0:
                if spline[0]=="###ENDNODE":
                    pos[0]=cur+1
                    return node
                else:
                    return None
            cur=cur+1
            
        return None
            
    class PP:
        def __init__(self):
            self.nodes=[]
            self.__template="""
###BEGINPP Puppet_ProfileMaker managed part, do not modify
%(nodestr)s
###ENDPP
"""
        def __str__(self):
            nodestr=""
            for i in self.nodes:
                nodestr=nodestr+str(i)
            return self.__template%{'nodestr':nodestr}

    class NODE:
        def __init__(self,vmid="",roleid="",hostname="",profile="",addr=""):
            self.vmid=vmid
            self.roleid=roleid
            self.hostname=hostname
            self.profile=profile
            self.addr=addr
            self.__template="""
###BEGINNODE %(vmid)s %(roleid)s %(hostname)s %(profile)s %(addr)s
node '%(hostname)s' {
 $webaddr="%(addr)s"
 $roleid="%(roleid)s"
 $vmid="%(vmid)s"
 include %(profile)s
}
###ENDNODE
"""
        def __str__(self):
            return self.__template%{'vmid':self.vmid,
                                    'roleid':self.roleid,
                                    'hostname':self.hostname,
                                    'profile':self.profile,
                                    'addr':self.addr}

    def readFromFile(self):
        inf=open(self.__nodesfile,"r")
        strarray=inf.readlines()
        inf.close()
        pos=[0]
        pp=self.readPPFromStr(strarray,pos)
        return pp

    def saveToFile(self,pp):
        ouf=open(self.__nodesfile,"w")
        ouf.write(str(pp))
        ouf.close()
        
    def provideMetaData(self,role):
        #for now, run puppetca to generate keys, and tar them up, set the base64 encoded value to MetaData field of the VM's attr
        self.debug("Puppet_ProfileMaker::provideMetaData::called. RoleID=%s, VMID=%s"%(role.Role_ID,role.vm.VM_ID))

        vmip=role.vm.getAttr("PrivateIPV4")

        if vmip==None:
            raise RuntimeError("Can't get PrivateIPV4 for vm name=%s id=%s, role name=%s id=%s"%(role.vm.name,role.vm.VM_ID,role.name,role.Role_ID))

        self.debug("vmip=%s"%vmip)
        
        #run puppetca
        cmdstr="puppetca --generate %s"%vmip
        (status,output)=commands.getstatusoutput(cmdstr)
        if status!=0:
            raise RuntimeError("Can't run cmdstr=%s, statuscode=%s, output=%s"%(cmdstr,status,output))

        #locate the files
        #/etc/puppet/ssl/ca/signed/172.19.2.3.pem
        #/etc/puppet/ssl/ca/ca_crt.pem
        #/etc/puppet/ssl/private_keys/172.19.2.3.pem
        #copy them to a tmp folder and put into (with ssl as the base dir)
        #/etc/puppet/ssl/certs/172.19.2.3.pem
        #/etc/puppet/ssl/certs/ca.pem
        #/etc/puppet/ssl/private_keys/172.19.2.3.pem

        myuuid=str(uuid.uuid4())
        workdir=tempfile.gettempdir()+"/"+myuuid
        ssldir=workdir+"/ssl"

        self.debug("myuuid=%s, workdir=%s, ssldir=%s"%(myuuid,workdir,ssldir))
        
        os.makedirs(ssldir,0700)
        os.makedirs(ssldir+"/certs",0700)
        os.makedirs(ssldir+"/private_keys",0700)
        
        shutil.copyfile("/etc/puppet/ssl/ca/signed/%s.pem"%vmip,"%s/certs/%s.pem"%(ssldir,vmip))
        shutil.copyfile("/etc/puppet/ssl/ca/ca_crt.pem","%s/certs/ca.pem"%ssldir)
        shutil.copyfile("/etc/puppet/ssl/private_keys/%s.pem"%vmip,"%s/private_keys/%s.pem"%(ssldir,vmip))

        olddir=os.getcwd()
        os.chdir(workdir)
        cmdstr="tar zc ssl | base64 -w 0 "
        (status,output)=commands.getstatusoutput(cmdstr)
        if status!=0:
            raise RuntimeError("Can't run cmdstr=%s, statuscode=%s, output=%s"%(cmdstr,status,output))
        os.chdir(olddir)

        role.vm.setAttr("MetaData",output)
        role.vm.setAttr("MetaDataProvided","True")
        
        
        
